"""
Author        yongfa
Date          2022-07-14 13:42:33
LastEditTime  2023-09-08 06:57:46
LastEditors   yongfa
Description   数据模型
"""


from app.extensions.sqlalchemy import db, EntityModel as Base, model_list_to_dict
from app.extensions.initialization.permission import api_map, menu_map, roles_permissions_map
from app.extensions import casbin_enforcer


# relationship table
roles_apis = db.Table('permission_roles_apis',
                      db.Column('role_id', db.Integer, db.ForeignKey('permission_role.id', ondelete='CASCADE')),
                      db.Column('apis', db.Integer, db.ForeignKey('permission_apis.id', ondelete='CASCADE'))
                      )


# relationship table
roles_menus = db.Table('permission_roles_menus',
                       db.Column('role_id', db.Integer, db.ForeignKey('permission_role.id', ondelete='CASCADE')),
                       db.Column('menu_id', db.Integer, db.ForeignKey('permission_menu.id', ondelete='CASCADE'))
                       )
# relationship table
roles_users = db.Table('permission_users_roles',
                       db.Column('user_id', db.Integer, db.ForeignKey('user.id', ondelete='CASCADE')),
                       db.Column('role_id', db.Integer, db.ForeignKey('permission_role.id', ondelete='CASCADE'))
                       )

# relationship table
roles_dicts = db.Table('permission_roles_dicts',
                       db.Column('role_id', db.Integer, db.ForeignKey('permission_role.id', ondelete='CASCADE')),
                       db.Column('dict_id', db.Integer, db.ForeignKey('dict.id', ondelete='CASCADE'))
                       )


class Apis(Base):
    __tablename__ = 'permission_apis'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(30), unique=True, comment='权限api名称')
    endpoint = db.Column(db.String(255), comment='权限api端点')  # 用作权限判断
    path = db.Column(db.String(100), comment='权限api地址')
    label = db.Column(db.String(50), comment='权限描述')
    group = db.Column(db.String(50), comment='权限分组')
    method = db.Column(db.String(10), comment='请求方法')
    parent_id = db.Column(db.Integer, comment='父级ID')
    roles = db.relationship('Role', secondary=roles_apis, back_populates='apis')

    @staticmethod
    def init_data():
        for item in api_map:
            perm = Apis.get(name=item['name'])

            if perm is None:
                perm = Apis.create(**item)
            else:
                perm.update(**item)
            children = item.pop('children') if item.get('children') else []
            for child in children:
                _child = Apis.get(name=child['name'])
                child['parent_id'] = perm.id
                if _child is None:
                    _child = Apis.create(**child)
                else:
                    _child.update(**child)

                children2 = child.pop('children') if child.get('children') else []
                for child2 in children2:
                    _child2 = Apis.get(name=child2['name'])
                    child2['parent_id'] = _child.id
                    if _child2 is None:
                        Apis.create(**child2)
                    else:
                        _child2.update(**child2)

    def to_dict(self, *args, **kwargs):
        res = super().to_dict(*args, **kwargs)
        return res

    def list_to_dict(self, *args, **kwargs):
        res = self.to_dict(*args, **kwargs)
        return res


class Menu(Base):

    __tablename__ = 'permission_menu'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(30), unique=True)
    parent_id = db.Column(db.Integer, comment='父级ID')
    label = db.Column(db.String(100), comment='菜单描述')
    path = db.Column(db.String(255), comment='菜单path')
    icon = db.Column(db.String(50), comment='菜单图标')
    component = db.Column(db.String(255), comment='对应前端文件路径')
    hidden = db.Column(db.Boolean, default=False, comment='是否在列表隐藏')
    sort = db.Column(db.Integer, comment='排序标记')
    meta = db.Column(db.JSON(), comment='附加信息')
    roles = db.relationship('Role', secondary=roles_menus, back_populates='menus')

    @staticmethod
    def init_data():
        for item in menu_map:
            menu = Menu.get(name=item['name'])
            if menu is None:
                menu = Menu.create(**item)
            else:
                menu.update(**item)
            children = item.pop('children') if item.get('children') else []
            for child in children:
                _child = Menu.get(name=child['name'])
                child['parent_id'] = menu.id
                if _child is None:
                    _child = Menu.create(**child)
                else:
                    _child.update(**child)

                children = child.pop('children') if child.get('children') else []
                for child2 in children:
                    _child2 = Menu.get(name=child2['name'])
                    child2['parent_id'] = _child.id
                    if _child2 is None:
                        Menu.create(**child2)
                    else:
                        _child2.update(**child2)

    def to_dict(self, *args, **kwargs):
        res = super().to_dict(*args, **kwargs)
        return res

    def list_to_dict(self, *args, **kwargs):
        res = self.to_dict(*args, **kwargs)
        return res


class Role(Base):

    __tablename__ = 'permission_role'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(30), unique=True)
    label = db.Column(db.String(100), comment='角色描述')
    parent_id = db.Column(db.Integer, comment='父级ID')
    # users = db.relationship('User', back_populates='role')
    apis = db.relationship('Apis', secondary=roles_apis, back_populates='roles')
    menus = db.relationship('Menu', secondary=roles_menus, back_populates='roles')
    users_list = db.relationship('User', secondary=roles_users, back_populates='roles')
    dicts = db.relationship('Dict', secondary=roles_dicts, back_populates='roles', lazy='dynamic')
    is_super_admin = db.Column(db.Boolean, default=False, comment='是否是超级管理员')

    @staticmethod
    def init_data():
        Apis.init_data()
        Menu.init_data()
        for role_name, role_data in roles_permissions_map.items():
            role = Role.get(name=role_name)
            if role is None:
                role = Role(name=role_name, label=role_name)
                role.flush()
            role.label = role_name
            role.apis = []
            items = {'menus': Menu, 'apis': Apis}
            for key, value in role_data.items():
                if key in items.keys():
                    for v in value:
                        item = items[key].get(label=v)
                        if item:
                            getattr(role, key).append(item)
                else:
                    setattr(role, key, value)
            role.reload_casbin_role()
            db.session.add(role)

        db.session.commit()

    def to_dict(self, *args, **kwargs):
        res = super().to_dict(*args, **kwargs)
        exclude_fields = kwargs.get('exclude_fields', [])
        output_fields = ['id', 'name', 'value']
        output_attrs = ['apis', 'menus', 'dicts']
        _output_fields = list(set(output_attrs).difference(set(exclude_fields)))
        results = self.get_all_permissions(result={}, attrs=_output_fields, role=self)
        for attr in _output_fields:
            res[attr] = model_list_to_dict(results.get(attr, []), output_fields=output_fields)
            # res[attr] = [item.id for item in results.get(attr, [])]
        return res

    def get_all_permissions(self, result: dict = {}, attrs: list = [], role: object = None):
        """获取所有下级的权限"""

        for attr in attrs:
            attr_data = getattr(role, attr)
            if result.get(attr) is None:
                result[attr] = []
            result[attr].extend(attr_data)
        children = role.children
        if children:
            for child in children:
                self.get_all_permissions(result, attrs, child)

        for attr in attrs:
            result[attr] = list(set(result.get(attr)))
        return result

    def get_children_id(self):
        roles = Role.get_all(parent_id=self.id)
        children_id = [role.id for role in roles]
        return children_id

    @property
    def parent(self):
        role = Role.get(id=self.parent_id)
        return role

    @property
    def children(self):
        children = Role.get_all(parent_id=self.id)
        return children

    @classmethod
    def get_common_role(cls):
        common_role = cls.get(name='普通用户')
        return common_role

    def reload_casbin_role(self):
        "若apis修改,重置该角色所有权限"
        casbin_enforcer.remove_filtered_policy(0, str(self.id))
        for api in self.apis:
            if api.method:
                casbin_enforcer.add_policy(str(self.id), api.path.replace('<int:id>', '*'), api.method)

        # 处理父节点 继承是级联的, 即父-子-孙，父节点也拥有孙节点的权限
        self.reload_parent_role()

    def reload_parent_role(self):
        parent = self.parent
        if parent:
            casbin_enforcer.remove_filtered_grouping_policy(1, str(self.id))
            casbin_enforcer.add_grouping_policy(str(parent.id), str(self.id))
